#include <Windows.h>
#include "3rd Party Headers/x64 Win/glew.h" // Opengl extensions wrangler

//#include "KeyboardCodes.h"
/***************************************************************************************
Operating system: Windoze
Platform: Win32 or x64
Character encoding: UNICODE
****************************************************************************************/
LRESULT CALLBACK WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
    switch (umsg)
    {
    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;
        break;
    case WM_KEYDOWN: 
        if ( lparam & (1 << 30) == 0 )
            
        break;
    case WM_KEYUP:
        
        break;
    default:
        return DefWindowProc(hwnd, umsg, wparam, lparam);
    }
    return 0;
}

bool InitializeExtensions(HWND & hwnd, HDC & deviceContext, HGLRC & renderContext)
{
    PIXELFORMATDESCRIPTOR pixelFormat;
    int error;
    deviceContext = GetDC(hwnd);
    if(deviceContext == NULL) return false;
    // Set a temporary default pixel format.
    error = SetPixelFormat(deviceContext, 1, &pixelFormat);
    if(error != 1) return false;
    // Create a temporary rendering context.
    renderContext = wglCreateContext(deviceContext);
    if(! renderContext) return false;
    // Set the temporary rendering context as the current rendering context for this window.
    error = wglMakeCurrent(deviceContext, renderContext);
    if(error != 1) return false;
    // Initialize the OpenGL extensions needed for this application.  Note that a temporary rendering context was needed to do so.
    glewInit();
    // Release the temporary rendering context now that the extensions have been loaded.
    wglMakeCurrent(NULL, NULL);
    wglDeleteContext(renderContext);
    renderContext = NULL;
    // Release the device context for this window.
    ReleaseDC(hwnd, deviceContext);
    deviceContext = 0;
    return true;
}

bool InitializeOpenGL(HWND & hwnd, HDC & deviceContext, HGLRC & renderContext)
{
    int pixelFormat[1];
    int result;
    deviceContext = GetDC(hwnd);
    if(! deviceContext) return false;
    PIXELFORMATDESCRIPTOR pfd =
    {
        sizeof(PIXELFORMATDESCRIPTOR),
        1,
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,    //Flags
        PFD_TYPE_RGBA,            //The kind of framebuffer. RGBA or palette.
        32,                        //Colordepth of the framebuffer.
        0, 0, 0, 0, 0, 0,
        0,
        0,
        0,
        0, 0, 0, 0,
        24,                        //Number of bits for the depthbuffer
        8,                        //Number of bits for the stencilbuffer
        0,                        //Number of Aux buffers in the framebuffer.
        PFD_MAIN_PLANE,
        0,
        0, 0, 0
    };
    pixelFormat[0] = ChoosePixelFormat(deviceContext, &pfd);
    // If the video card/display can handle our desired pixel format then we set it as the current one.
    result = SetPixelFormat(deviceContext, pixelFormat[0], &pfd);
    if(result != 1) return false;
    renderContext = wglCreateContext(deviceContext);
    if(! renderContext) return false;
    // Set the rendering context to active.
    result = wglMakeCurrent(deviceContext, renderContext);
    return true;
}

bool InitializeWindow(HWND & hwnd, HINSTANCE & hinstance, HDC & deviceContext, HGLRC & renderContext, 
                      int windowWidth, int windowHeight, int screenWidth, int screenHeight,
                      int posX, int posY)
{
    const wchar_t* applicationName = L"Engine";
    WNDCLASSEX wc;
    DEVMODE dmScreenSettings;
    bool result;
    wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hinstance;
    wc.hIcon         = LoadIcon(NULL, IDI_WINLOGO);
    wc.hIconSm       = wc.hIcon;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = applicationName;
    wc.cbSize        = sizeof(WNDCLASSEX);
    RegisterClassExW(&wc);
    // Create a temporary window for the OpenGL extension setup.
    hwnd = CreateWindowEx(WS_EX_APPWINDOW, applicationName, applicationName, WS_POPUP,
    0, 0, 640, 480, NULL, NULL, hinstance, NULL);
    if(hwnd == NULL) return false;
    ShowWindow(hwnd, SW_HIDE);
    // Initialize a temporary OpenGL window and load the OpenGL extensions.
    result = InitializeExtensions(hwnd, deviceContext, renderContext);
    if(! result)
    {
        MessageBox(hwnd, L"Could not initialize the OpenGL extensions.", L"Error", MB_OK);
        return false;
    }
    DestroyWindow(hwnd); hwnd = NULL;
    /*
    memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
    dmScreenSettings.dmSize       = sizeof(dmScreenSettings);
    dmScreenSettings.dmPelsWidth  = (unsigned long)screenWidth;
    dmScreenSettings.dmPelsHeight = (unsigned long)screenHeight;
    dmScreenSettings.dmBitsPerPel = 32;			
    dmScreenSettings.dmFields     = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
    // Change the display settings to full screen.
    ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN); 
    */
    // Create the window with the screen settings and get the handle to it.
    hwnd = CreateWindowEx(WS_EX_APPWINDOW, applicationName, applicationName, WS_POPUP,
    posX, posY, windowWidth, windowHeight, NULL, NULL, hinstance, NULL);
    if(hwnd == NULL) return false;
    // Initialize OpenGL now that the window has been created.
    result = InitializeOpenGL(hwnd, deviceContext, renderContext);
    if(! result)
    {
        MessageBox(hwnd, L"Could not initialize OpenGL, check if video card supports OpenGL 4.0.", L"Error", MB_OK);
        return false;
    }
    // Bring the window up on the screen and set it as main focus.
    ShowWindow(hwnd, SW_SHOW);
    SetForegroundWindow(hwnd);
    SetFocus(hwnd);        
    // Hide the mouse cursor.
    ShowCursor(false);
    return true;
}

int WINAPI wWinMain(HINSTANCE thisInstance, HINSTANCE prevInstance, LPWSTR cmdLine, int cmdShow)
{
    int screenWidth = GetSystemMetrics(SM_CXSCREEN);
    int screenHeight = GetSystemMetrics(SM_CYSCREEN);
    int windowWidth, windowHeight, posX, posY;
#ifdef _DEBUG
        windowWidth = 640;
        windowHeight = 480;
        posX = screenWidth - windowWidth;
        posY = windowHeight;
#else
        windowWidth = screenWidth;
        windowHeight = screenHeight;
        posX = posY = 0;
#endif
    bool done = false;
    HWND hwnd;
    HINSTANCE hinstance;
    HDC deviceContext;
    HGLRC renderContext;
    MSG msg;
    hinstance = thisInstance;
    InitializeWindow(hwnd, hinstance, deviceContext, renderContext, 
                     windowWidth, windowHeight, screenWidth, screenHeight, posX, posY);

    while (! done)
    {
        // Handle the windows messages.
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            // If windows signals to end the application then exit out.
            if(msg.message == WM_QUIT) 
                done = true;
        }
    }
    return 1;
}